package gu.sql2java.bean;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;

/**
 * 重写{@link #getWriteMethod()}方法，允许返回类型不为void的write方法
 * @author guyadong
 *
 */
public class LenientDecoratorOfDescriptor extends PropertyDescriptor {
	private final PropertyDescriptor descriptor;

	private LenientDecoratorOfDescriptor(PropertyDescriptor descriptor) throws IntrospectionException {
		super(descriptor.getName(), 
				classOf(descriptor),
				readMethodNameOf(descriptor),
				writeMethodNameOf(descriptor));
		this.descriptor = descriptor;
	}
	private static String readMethodNameOf(PropertyDescriptor descriptor){
		Method m = descriptor.getReadMethod();
		return null == m ? null : m.getName();
	}
	private static String writeMethodNameOf(PropertyDescriptor descriptor){
		Method m = descriptor.getWriteMethod();
		return null == m ? null : m.getName();
	}
	/**
	 * 反射方法调用{@code FeatureDescriptor.getClass0()} 方法
	 * @param descriptor
	 * @return class0 result
	 */
	private static Class<?> classOf(PropertyDescriptor descriptor){
		try {
			Method m =  (Method) internalFindMethod(descriptor.getClass(),"getClass0");
			m.setAccessible(true);
			return (Class<?>) m.invoke(descriptor);
		} catch (NullPointerException e) {
			throw e;
		}catch (RuntimeException e) {
			throw e;
		} catch (Exception e) {
			throw new RuntimeException(e);
		} 
	}
    /**
     * Returns a String which capitalizes the first letter of the string.
     */
    private static String capitalize(String name) {
        if (name == null || name.length() == 0) {
            return name;
        }
        return name.substring(0, 1).toUpperCase() + name.substring(1);
    }
    @Override
	public synchronized Method getWriteMethod() {
		Method writeMethod = super.getWriteMethod();
		if (writeMethod == null) {
			// 先获取read方法,如果classRef为null,会用read方法所属的类初始化
			Method readMethod = getReadMethod();
			Class<?> cls = classOf(this);
			if(cls != null){
				String writeMethodName = "set" + capitalize(getName());
				Class<?>readReturnType = null;
				if(readMethod != null){
					readReturnType = readMethod.getReturnType();
				}
				// 遍历所有方法查找指定名字的方法,
				// 如果存在read方法，匹配只有一个参数且参数类型与读取方法的返回类型一致的方法
				// 如果不存在read方法，匹配只有一个参数方法
				for(Method method : cls.getMethods()){
					Class<?>[] parameterTypes = method.getParameterTypes();
					if(method.getName().equals(writeMethodName) && parameterTypes.length == 1){
						try{
							if(readReturnType != null){
								// 如果有read方法,则参数类型必须与read方法返回值类型一致
								if(parameterTypes[0].equals(readReturnType)){
									setWriteMethod(method);
									writeMethod = method;
									break;
								}
							}else{
								setWriteMethod(method);
								writeMethod = method;
								break;
							}
						} catch (IntrospectionException e) {
							// fall through
						}
					}
				}
			}
		}
		return writeMethod;
	}
	public PropertyDescriptor origin() {
		return descriptor;
	}
	public static LenientDecoratorOfDescriptor toDecorator(PropertyDescriptor descriptor){
		if(descriptor == null){
			return null;
		}
		if(descriptor instanceof LenientDecoratorOfDescriptor){
			return (LenientDecoratorOfDescriptor)descriptor;
		}
		try {
			return new LenientDecoratorOfDescriptor(descriptor);
		} catch (IntrospectionException e) {
			throw new RuntimeException(e);
		}
	}
    /**
     * Internal support for finding a target methodName with a given
     * parameter list on a given class.
     */
    private static Method internalFindMethod(Class<?> start, String methodName,Class<?> ...args) {

        Method method = null;

        for (Class<?> cl = start; cl != null && cl.getDeclaringClass() != Object.class && method == null; cl = cl.getSuperclass()) {
        	try {
        		method = cl.getDeclaredMethod(methodName, args);
        	} catch (NoSuchMethodException e) {
        	} 
        }
        return method;
    }

}
